package com.springboot.jdk.socket;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.util.Iterator;


/**
 * NIO 客户端程序重构
 *
 * @author: zr
 * @date: 2021-10-21
 */
public class NioClient {

    private static CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();
    private static CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder();

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            // 启动10个线程
            new Worker().start();
        }
    }

    static class Worker extends Thread {

        @Override
        public void run() {
            SocketChannel channel = null;
            Selector selector = null;
            try {
                // 构建一个SocketChannel, 底层可以看做是封装了一个Socket
                channel = SocketChannel.open();
                // SocketChannel 是连接到底层的Socket网络连接上去的数据通道
                // 就是负责网络读写数据的
                // 配置成非阻塞的
                channel.configureBlocking(false);

                // 绑定端口号和ip
                channel.connect(new InetSocketAddress("localhost", 9000));
                // 底层一定是发起了一个TCP三次握手，尝试建立连接
                // 他在后台一定是跟server端进行三次握手，如果握手成功之后，
                // 就是说建立了一个连接

                selector = Selector.open();
                // 监听 connection 连接
                channel.register(selector, SelectionKey.OP_CONNECT);
                boolean isOver = false;

                while (!isOver) {
                    // 服务端程序一定会给客户端返回一个响应
                    selector.select();
                    // 一个SelectionKey 代表了一个请求
                    Iterator ite = selector.selectedKeys().iterator();
                    while (ite.hasNext()) {
                        SelectionKey key = (SelectionKey) ite.next();
                        ite.remove();
                        // 如果说 server 返回的是一个connectable 的消息
                        if (key.isConnectable()) {
                            // ConnectionPending 表示正在连接
                            if (channel.isConnectionPending()) {
                                // 判断一下，如果说连接已经成功
                                // 即TCP三次握手已经成功了
                                if (channel.finishConnect()) {
                                    // 一旦跟server端建立好了连接之后
                                    key.interestOps(SelectionKey.OP_READ);
                                    // 接下来对这个SocketChannel
                                    ByteBuffer allocate = ByteBuffer.allocate(1024);
                                    allocate.put("【客户端】：连接建立成功".getBytes());
                                    allocate.flip();
                                    channel.write(allocate);
                                    channel.register(selector, SelectionKey.OP_READ);
                                }
                            }
                        } else if (key.isReadable()) {
                            // 就说明服务器返回了一条数据就可以读取了
                            ByteBuffer byteBuffer = ByteBuffer.allocate(128);
                            // 把数据写入buffer， position 推进到读取字节数的大小
                            int len = channel.read(byteBuffer);
                            // limit = 当前position 的大小，position = 0, 复位

                            if (len > 0) {
                                System.out.println("[" + Thread.currentThread().getName() + "]接收到响应，" + new String(byteBuffer.array(), 0, len));
                                Thread.sleep(5000);
                                channel.register(selector, SelectionKey.OP_READ);
                            }
                        } else if (key.isWritable()) {
                            // 对某个意思SocketChannel 想写一条数据回去
                            ByteBuffer buffer = ByteBuffer.allocate(1024);
                            buffer.put("【客户端】：发送数据，你好".getBytes());
                            buffer.flip();

                            channel = (SocketChannel) key.channel();
                            channel.write(buffer);
                            channel.register(selector, SelectionKey.OP_READ);
                        }
                    }
                }
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (channel != null) {
                    try {
                        channel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

                if (selector != null) {
                    try {
                        selector.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        private String getWord() {
            return "客户端发送响应数据给服务端";
        }

    }
}
